package orm

import (
	"context"
	"reflect"

	"gitee.com/geektime-geekbang/geektime-go/orm/homework2/internal/errs"
)

type Updater[T any] struct {
	builder
	db      *DB
	assigns []Assignable
	val     *T
	where   []Predicate
}

func NewUpdater[T any](db *DB) *Updater[T] {
	// panic("implement me")
	return &Updater[T]{
		builder: builder{
			dialect: db.dialect,
			quoter:  db.dialect.quoter(),
		},
		db: db,
	}
}

func (u *Updater[T]) Update(t *T) *Updater[T] {
	// panic("implement me")
	u.val = t
	return u
}

func (u *Updater[T]) Set(assigns ...Assignable) *Updater[T] {
	u.assigns = assigns
	return u
}

func (u *Updater[T]) Build() (*Query, error) {
	// panic("implement me")
	if len(u.assigns) == 0 {
		return nil, errs.ErrNoUpdatedColumns
	}

	var (
		t   T
		err error
	)
	u.builder.model, err = u.db.r.Get(&t)
	if err != nil {
		return nil, err
	}

	u.builder.sb.WriteString("UPDATE ")
	tableName := u.builder.model.TableName
	u.builder.quote(tableName)
	u.builder.sb.WriteString(" SET ")

	u.builder.args = make([]any, 0, len(u.builder.model.FieldMap))
	for idx, assign := range u.assigns {
		if idx > 0 {
			u.builder.sb.WriteByte(',')
		}

		switch a := assign.(type) {
		case Column:
			fd, ok := u.builder.model.FieldMap[a.name]
			if !ok {
				return nil, errs.NewErrUnknownField(a.name)
			}
			u.builder.quote(fd.ColName)
			u.builder.sb.WriteString("=?")

			refVal := reflect.ValueOf(u.val).Elem()
			u.builder.addArgs(refVal.FieldByName(a.name).Interface())

		case Assignment:
			colName := a.column
			val := a.val
			fd, ok := u.builder.model.FieldMap[colName]
			if !ok {
				return nil, errs.NewErrUnknownField(colName)
			}

			u.builder.quote(fd.ColName)
			// u.builder.sb.WriteString("=?")
			switch v := val.(type) {
			case value:
				u.builder.sb.WriteString("=?")
				u.builder.addArgs(v.val)

			case MathExpr:
				if v.op == "+" {
					u.builder.sb.WriteByte('=')
					colName := v.left.(Column).name
					u.builder.quote(u.builder.model.FieldMap[colName].ColName)
					u.builder.sb.WriteString(" + ?")
				}
				u.builder.addArgs(v.right.(value).val)

			case RawExpr:
				u.builder.sb.WriteByte('=')
				u.builder.sb.WriteString(v.raw)
				u.builder.addArgs(v.args...)

			default:
				return nil, errs.NewErrUnsupportedExpressionType(v)
			}
			// u.builder.addArgs(val.(value).val)
		default:
			return nil, errs.NewErrUnsupportedAssignableType(a)
		}
	}

	if len(u.where) > 0 {
		u.builder.sb.WriteString(" WHERE ")
		if err = u.builder.buildPredicates(u.where); err != nil {
			return nil, err
		}
	}

	u.builder.sb.WriteString(";")
	return &Query{
		SQL:  u.builder.sb.String(),
		Args: u.builder.args,
	}, nil
}

func (u *Updater[T]) Where(ps ...Predicate) *Updater[T] {
	u.where = ps
	return u
}

func (u *Updater[T]) Exec(ctx context.Context) Result {
	panic("implement me")
}

// AssignNotZeroColumns 更新非零值
func AssignNotZeroColumns(entity interface{}) []Assignable {
	panic("implement me")
}
